using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace System.Windows.Forms
{
    /// <summary>
    /// Summary description for Shape.
    /// </summary>
    [ToolboxBitmap(typeof(Shape), "Shape.ico")]
    [DesignerCategory("Form")]
    public class Shape : System.Windows.Forms.ContainerControl, ISupportInitialize
    {
        //private Shapes.Appearance appearance = new Shapes.Appearance();
        private Brush b;
        private System.ComponentModel.Container components = null;
        private Fill fill = new Fill();
        private bool initializing = false;
        private Line line = new Line();
        private Pen p;
        private GraphicsPath path, purePath;
        private bool regular = false;
        private float rotation = 0f;
        private ShapeStyle shape = ShapeStyle.Rectangle;
        private ContentAlignment textAlign = ContentAlignment.MiddleCenter;
        private bool wordWrap = false;

        /// <summary>
        /// Initializes a new instance of the <see cref="Shape"/> class.
        /// </summary>
        public Shape()
        {
            SetStyle(ControlStyles.ResizeRedraw, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            // This call is required by the Windows.Forms Form Designer.
            InitializeComponent();

            AddFillEvents();
            AddLineEvents();
        }

        [Category("Property Changed")]
        public event EventHandler FillChanged;

        [Category("Property Changed")]
        public event EventHandler LineChanged;

        [Category("Property Changed")]
        public event EventHandler RestrictToRegularChanged;

        [Category("Property Changed")]
        public event EventHandler RotationChanged;

        [Category("Property Changed")]
        public event EventHandler ShapeStyleChanged;

        [Category("Property Changed")]
        public event EventHandler TextAlignChanged;

        [Category("Property Changed")]
        public event EventHandler WordWrapChanged;

        /// <summary>
        /// Background color set to transparent and hidden
        /// </summary>
        [DefaultValue(typeof(Color), "Transparent")]
        public override System.Drawing.Color BackColor
        {
            get { return base.BackColor; }
            set { base.BackColor = value; }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Shape")]
        public Fill Fill
        {
            get { return fill; }
            set {
                System.Diagnostics.Trace.WriteLine("Set Shape.Fill with " + value.Style);
                RemoveFillEvents();
                fill = (Fill)value.Clone();
                AddFillEvents();
                if (!initializing)
                    OnFillChanged(new EventArgs());
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Shape")]
        public Line Line
        {
            get { return line; }
            set
            {
                RemoveLineEvents();
                line = (Line)value.Clone();
                AddLineEvents();
                if (!initializing)
                    OnLineChanged(new EventArgs());
            }
        }

        [DefaultValue(false)]
        [Category("Shape")]
        public bool RestrictToRegular
        {
            get { return regular; }
            set
            {
                if (regular != value)
                {
                    regular = value;
                    if (!initializing)
                        OnRestrictToRegularChanged(new EventArgs());
                }
            }
        }

        [DefaultValue(0f)]
        [Category("Shape")]
        [Editor(typeof(DegreeTypeEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public float Rotation
        {
            get { return rotation; }
            set
            {
                if (rotation != value)
                {
                    rotation = value;
                    if (!initializing)
                        OnRotationChanged(new EventArgs());
                }
            }
        }

        [DefaultValue(ShapeStyle.Rectangle),
        Category("Appearance")]
        [Editor(typeof(ShapeStyleEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public ShapeStyle ShapeStyle
        {
            get { return shape; }
            set
            {
                if (shape != value)
                {
                    shape = value;
                    if (!initializing)
                        OnShapeStyleChanged(new EventArgs());
                }
            }
        }

        /// <summary>
        /// General appearance of the shape
        /// </summary>
        /*[Category("Appearance")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        private Shapes.Appearance Style
        {
            get { return appearance; }
        }*/

        [DefaultValue(ContentAlignment.MiddleCenter),
        Category("Appearance")]
        public ContentAlignment TextAlign
        {
            get { return textAlign; }
            set
            {
                if (textAlign != value)
                {
                    textAlign = value;
                    if (!initializing)
                        OnTextAlignChanged(new EventArgs());
                }
            }
        }

        [DefaultValue(false)]
        [Category("Appearance")]
        public bool WordWrap
        {
            get { return wordWrap; }
            set
            {
                if (wordWrap != value)
                {
                    wordWrap = value;
                    if (!initializing)
                        OnWordWrapChanged(new EventArgs());
                }
            }
        }

        private Rectangle PaintRect
        {
            get
            {
                Rectangle r = this.DisplayRectangle;
                //r.Inflate(-(lineWidth+1)/2, -(lineWidth+1)/2);
                r.Offset(1,1); r.Width--; r.Height--;
                return r;
            }
        }

        public void BeginInit()
        {
            initializing = true;
        }

        public void EndInit()
        {
            initializing = false;
            UpdatePen();
            UpdatePath();
        }

        internal void PaintShape(Graphics graphics)
        {
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            if (b != null)
                graphics.FillPath(b, path);
            if (line.Width > 0)
            {
                if (p == null) UpdatePen();
                graphics.DrawPath(p, path);
            }
            if (base.Text.Length > 0)
            {
                graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                RectangleF bnds = path.GetBounds();
                StringFormat sf = StringFormat.GenericDefault;
                sf.FormatFlags = StringFormatFlags.NoClip;
                if (wordWrap == false)
                    sf.FormatFlags |= StringFormatFlags.NoWrap;
                for (int la = 2; la >= 0; la--)
                {
                    int ta = (int)textAlign >> (4 * la);
                    if (ta > 0)
                    {
                        sf.LineAlignment = (StringAlignment)la;
                        sf.Alignment = (StringAlignment)(ta >> 1);
                        break;
                    }
                }
                graphics.DrawString(base.Text, base.Font, new SolidBrush(base.ForeColor), bnds, sf);
            }
        }

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        protected void OnFillChanged(EventArgs e)
        {
            UpdateBrush();
            if (FillChanged != null)
                FillChanged(this, e);
            if (this.IsHandleCreated)
                Invalidate();
        }

        protected void OnFillEvent(object obj, EventArgs e)
        {
            if (!initializing)
                OnFillChanged(e);
        }

        protected override void OnForeColorChanged(System.EventArgs e)
        {
            UpdatePen();
            base.OnForeColorChanged(e);
        }

        protected void OnLineChanged(EventArgs e)
        {
            UpdatePen();
            if (LineChanged != null)
                LineChanged(this, e);
            Invalidate();
        }

        protected void OnLineEvent(object obj, EventArgs e)
        {
            if (!initializing)
                OnLineChanged(e);
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaint(e);
            PaintShape(e.Graphics);
        }

        protected override void OnParentChanged(System.EventArgs e)
        {
            base.OnParentChanged(e);
        }

        protected void OnRestrictToRegularChanged(EventArgs e)
        {
            UpdatePath();
            if (RestrictToRegularChanged != null)
                RestrictToRegularChanged(this, e);
            Invalidate();
        }

        protected void OnRotationChanged(EventArgs e)
        {
            UpdatePath();
            if (RotationChanged != null)
                RotationChanged(this, e);
            Invalidate();
        }

        protected void OnShapeStyleChanged(EventArgs e)
        {
            UpdateShape();
            UpdatePath();
            if (ShapeStyleChanged != null)
                ShapeStyleChanged(this, e);
            Invalidate();
        }

        protected override void OnSizeChanged(System.EventArgs e)
        {
            UpdatePath();
            base.OnSizeChanged(e);
        }

        protected void OnTextAlignChanged(EventArgs e)
        {
            if (TextAlignChanged != null)
                TextAlignChanged(this, e);
            Invalidate();
        }

        protected override void OnTextChanged(System.EventArgs e)
        {
            base.OnTextChanged(e);
            Invalidate();
        }

        protected void OnWordWrapChanged(EventArgs e)
        {
            if (WordWrapChanged != null)
                WordWrapChanged(this, e);
            Invalidate();
        }

        private void AddFillEvents()
        {
            if (fill != null)
            {
                fill.Color1Changed += new EventHandler(OnFillEvent);
                fill.GradientAngleChanged += new EventHandler(OnFillEvent);
                fill.GradientCenterChanged += new EventHandler(OnFillEvent);
                fill.Color2Changed += new EventHandler(OnFillEvent);
                fill.StyleChanged += new EventHandler(OnFillEvent);
                fill.TransparencyChanged += new EventHandler(OnFillEvent);
                fill.ImageChanged += new EventHandler(OnFillEvent);
                fill.PatternChanged += new EventHandler(OnFillEvent);
                fill.ImageWrapModeChanged += new EventHandler(OnFillEvent);
            }
        }

        private void AddLineEvents()
        {
            if (line != null)
            {
                line.ColorChanged += new EventHandler(OnLineEvent);
                line.JoinChanged += new EventHandler(OnLineEvent);
                line.StyleChanged += new EventHandler(OnLineEvent);
                line.WidthChanged += new EventHandler(OnLineEvent);
            }
        }

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            //
            // Shape
            //
            this.BackColor = Color.FromKnownColor(System.Drawing.KnownColor.Transparent);
        }

        private void RemoveFillEvents()
        {
            if (fill != null)
            {
                fill.Color1Changed -= new EventHandler(OnFillEvent);
                fill.GradientAngleChanged -= new EventHandler(OnFillEvent);
                fill.GradientCenterChanged -= new EventHandler(OnFillEvent);
                fill.Color2Changed -= new EventHandler(OnFillEvent);
                fill.StyleChanged -= new EventHandler(OnFillEvent);
                fill.TransparencyChanged -= new EventHandler(OnFillEvent);
                fill.ImageChanged -= new EventHandler(OnFillEvent);
                fill.PatternChanged -= new EventHandler(OnFillEvent);
                fill.ImageWrapModeChanged -= new EventHandler(OnFillEvent);
            }
        }

        private void RemoveLineEvents()
        {
            if (line != null)
            {
                line.ColorChanged -= new EventHandler(OnLineEvent);
                line.JoinChanged -= new EventHandler(OnLineEvent);
                line.StyleChanged -= new EventHandler(OnLineEvent);
                line.WidthChanged -= new EventHandler(OnLineEvent);
            }
        }

        private void UpdateBrush()
        {
            b = fill.GetBrush(PaintRect, path);
        }

        private void UpdatePath()
        {
            if (purePath == null) UpdateShape();
            path = (GraphicsPath)purePath.Clone();

            // Rotate and move to client coords
            Matrix matrix = new Matrix();
            matrix.Rotate(rotation, MatrixOrder.Append);

            // Scale to fill full unit box
            RectangleF bnds = path.GetBounds(matrix);
            System.Diagnostics.Trace.WriteLine(String.Format("Bounds1: {0}", bnds));
            float max = System.Math.Max(bnds.Width, bnds.Height);
            matrix.Scale(1f/max, 1f/max, MatrixOrder.Append);

            // Scale to fit client box
            bnds = path.GetBounds(matrix);
            System.Diagnostics.Trace.WriteLine(String.Format("Bounds2: {0}", bnds));
            Rectangle rect = PaintRect;
            if (regular)
            {
                max = Math.Min((float)rect.Width/bnds.Width, (float)rect.Height/bnds.Height);
                matrix.Scale(max, max, MatrixOrder.Append);
            }
            else
            {
                matrix.Scale((float)rect.Width/bnds.Width, (float)rect.Height/bnds.Height, MatrixOrder.Append);
            }

            /*/ Scale again using pen width
            path.Transform(matrix);
            matrix.Reset();
            bnds = path.GetBounds(matrix, p);
            System.Diagnostics.Trace.WriteLine(String.Format("Bounds3: {0}", bnds));
            if (regular)
            {
                max = Math.Min((float)rect.Width/bnds.Width, (float)rect.Height/bnds.Height);
                matrix.Scale(max, max, MatrixOrder.Append);
            }
            else
            {
                matrix.Scale((float)rect.Width/bnds.Width, (float)rect.Height/bnds.Height, MatrixOrder.Append);
            }*/

            // Center in client box
            bnds = path.GetBounds(matrix);
            System.Diagnostics.Trace.WriteLine(String.Format("Bounds4: {0}", bnds));
            if (bnds.Height > bnds.Width)
                matrix.Translate(((rect.Width-bnds.Width)/2f)-bnds.X, -bnds.Y, MatrixOrder.Append);
            else if (bnds.Width > bnds.Height)
                matrix.Translate(-bnds.X, ((rect.Height-bnds.Height)/2f)-bnds.Y, MatrixOrder.Append);
            else
                matrix.Translate(-bnds.X, -bnds.Y, MatrixOrder.Append);

            path.Transform(matrix);
            UpdateBrush();
        }

        private void UpdatePen()
        {
            p = line.GetPen();
        }

        private void UpdateShape()
        {
            purePath = ShapePathProvider.GetShapePath(shape);
        }
    }
}